﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MLMBonus.Engine
{
    /// <summary>
    /// This is a base class that implements INode and includes common events that can be subscribed to an a per-INode
    /// basis. When using this class, you only really need to override "RunPreProcess", "RunPromote", and 
    /// "RunPostProcess" with the logic needed. The relevant events are called automatically during the relevant
    /// INode methods.
    /// </summary>
    public abstract class NodeBase : INode
    {
        public NodeBase()
        {
            this.Volumes = new List<IVolume>();
        }
        public NodeBase(IBonusContext context) : this()
        {
            this.Context = context;
        }
        #region Events
        /// <summary>
        /// This event is called before RunPreProcess is called for this INode.
        /// </summary>
        public event EventHandler PreProcessing;
        /// <summary>
        /// This event is called after RunPreProcess has finished for this INode.
        /// </summary>
        public event EventHandler PreProcessed;
        /// <summary>
        /// This event is called before RunPromote is called for this INode.
        /// </summary>
        public event EventHandler Promoting;
        /// <summary>
        /// This event is called after RunPromote has finished for this INode.
        /// </summary>
        public event EventHandler Promoted;
        /// <summary>
        /// This event is called before Pay is called for each IVolume associated with the INode.
        /// </summary>
        public event EventHandler Paying;
        /// <summary>
        /// This event is called after all Pay methods have been called for all IVolume objects associated with the INode.
        /// </summary>
        public event EventHandler Paid;
        /// <summary>
        /// This event is called before RunPostProcess is called for this INode
        /// </summary>
        public event EventHandler PostProcessing;
        /// <summary>
        /// This event is called after RunPostProcess has finished for this INode
        /// </summary>
        public event EventHandler PostProcessed;

        protected void OnPreProcessing() 
        {
            EventHandler preProcessing = PreProcessing;
            if (preProcessing != null)
            {
                preProcessing(this, new EventArgs());
            }
        }
        protected void OnPreProcessed()
        {
            EventHandler preProcessed = PreProcessed;
            if (preProcessed != null)
            {
                preProcessed(this, new EventArgs());
            }
        }
        protected void OnPromoting()
        {
            EventHandler promoting = Promoting;
            if (promoting != null)
            {
                promoting(this, new EventArgs());
            }
        }
        protected void OnPromoted()
        {
            EventHandler promoted = Promoted;
            if (promoted != null)
            {
                promoted(this, new EventArgs());
            }
        }
        protected void OnPaying()
        {
            EventHandler paying = Paying;
            if (paying != null)
            {
                paying(this, new EventArgs());
            }
        }
        protected void OnPaid()
        {
            EventHandler paid = Paid;
            if (paid != null)
            {
                paid(this, new EventArgs());
            }
        }
        protected void OnPostProcessing()
        {
            EventHandler postProcessing = PostProcessing;
            if (postProcessing != null)
            {
                postProcessing(this, new EventArgs());
            }
        }
        protected void OnPostProcessed()
        {
            EventHandler postProcessed = PostProcessed;
            if (postProcessed != null)
            {
                postProcessed(this, new EventArgs());
            }
        }
        #endregion

        #region INode Members
        public string Id { get; set; }
        public string ParentId { get; set; }
        public bool IsPreProcessed { get; set; }
        public INode Parent { get; set; }
        public LinkedListNode<INode> ParentNode { get; set; }
        private LinkedList<INode> children = new LinkedList<INode>();
        public LinkedList<INode> Children
        {
            get
            {
                return children;
            }
            set
            {
                children = value;
            }
        }
        /// <summary>
        /// A list of IVolume objects for this Node.
        /// </summary>
        public List<IVolume> Volumes { get; set; }
        /// <summary>
        /// The BonusContext object used to pay and promote the distributor
        /// </summary>
        public IBonusContext Context { get; set; }
        /// <summary>
        /// Calls relevant events before and after RunPreProcess.
        /// </summary>
        /// <param name="BonusRunType">
        /// BonusRunType is used so that multiple run types can be handled by a single bonus module. It is expected that
        /// BonusRunType be used inside the method to determine the correct logic to be run at a given time.
        /// </param>
        public virtual void PreProcess(string BonusRunType)
        {
            OnPreProcessing();
            RunPreProcess(BonusRunType);
            OnPreProcessed();
        }
        /// <summary>
        /// Calls relevant events before and after RunPromote.
        /// </summary>
        /// <param name="BonusRunType">
        /// BonusRunType is used so that multiple run types can be handled by a single bonus module. It is expected that
        /// BonusRunType be used inside the method to determine the correct logic to be run at a given time.
        /// </param>
        public virtual void Promote(string BonusRunType)
        {
            OnPromoting();
            RunPromote(BonusRunType);
            OnPromoted();
        }
        /// <summary>
        /// Calls relevant events before calling the Pay method for each IVolume in Volumes.
        /// </summary>
        /// <param name="BonusRunType">
        /// BonusRunType is used so that multiple run types can be handled by a single bonus module. It is expected that
        /// BonusRunType be used inside the method to determine the correct logic to be run at a given time.
        /// </param>
        public virtual void Pay(string BonusRunType)
        {
            OnPaying();
            if (Volumes != null)
            {
                foreach (IVolume volume in Volumes)
                {
                    if (!volume.Pay(BonusRunType, this) && this.Parent != null)
                    {
                        this.Parent.Volumes.Add(volume);
                    }
                }
            }
            OnPaid();
        }
        /// <summary>
        /// Calls relevant events before and after RunPostProcess.
        /// </summary>
        /// <param name="BonusRunType">
        /// BonusRunType is used so that multiple run types can be handled by a single bonus module. It is expected that
        /// BonusRunType be used inside the method to determine the correct logic to be run at a given time.
        /// </param>
        public virtual void PostProcess(string BonusRunType)
        {
            OnPostProcessing();
            RunPostProcess(BonusRunType);
            OnPostProcessed();
        }
        #endregion

        /// <summary>
        /// Override to implement PreProcess logic for each INode. This can be used to initialize relevant values and
        /// pre-calculate things like initial personal volumes.
        /// </summary>
        /// <param name="BonusRunType">
        /// BonusRunType is used so that multiple run types can be handled by a single bonus module. It is expected that
        /// BonusRunType be used inside the method to determine the correct logic to be run at a given time.
        /// </param>
        protected abstract void RunPreProcess(string BonusRunType);
        /// <summary>
        /// Override to implement promotion logic for each INode.
        /// </summary>
        /// <param name="BonusRunType">
        /// BonusRunType is used so that multiple run types can be handled by a single bonus module. It is expected that
        /// BonusRunType be used inside the method to determine the correct logic to be run at a given time.
        /// </param>
        protected abstract void RunPromote(string BonusRunType);
        /// <summary>
        /// Override to implement PostProcess logic for each INode. This can be used to clean up the object and fire off
        /// relevant post-processing events.
        /// </summary>
        /// <param name="BonusRunType">
        /// BonusRunType is used so that multiple run types can be handled by a single bonus module. It is expected that
        /// BonusRunType be used inside the method to determine the correct logic to be run at a given time.
        /// </param>
        protected abstract void RunPostProcess(string BonusRunType);
    }
}
